/*
Autor: Alexander Savochkin, 2010
New BSD License
*/

#include <boost/spirit/include/lex_lexertl.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include "CommandParser.h"
#include "CommandASTActions.h"

using namespace boost::spirit;

using namespace SRCLF::ClientRequestParsing::AST;

BOOST_FUSION_ADAPT_STRUCT(
	RegulatorInfoCommand,
	(std::string, value)
)

BOOST_FUSION_ADAPT_STRUCT(
	RegulatorControlCommand,
	(std::string, value)
)

BOOST_FUSION_ADAPT_STRUCT(
	RegulatorAdminCommand,
	(std::string, value)
)

BOOST_FUSION_ADAPT_STRUCT(
	InputInfoCommand,
	(std::string, value)
)

BOOST_FUSION_ADAPT_STRUCT(
	InputControlCommand,
	(std::string, value)
)

BOOST_FUSION_ADAPT_STRUCT(
	InputAdminCommand,
	(std::string, value)
)

BOOST_FUSION_ADAPT_STRUCT(
	OutputSetCommand,
	(double, value)
)

BOOST_FUSION_ADAPT_STRUCT(
	OutputInfoCommand,
	(std::string, value)
)

BOOST_FUSION_ADAPT_STRUCT(
	OutputControlCommand,
	(std::string, value)
)

BOOST_FUSION_ADAPT_STRUCT(
	OutputAdminCommand,
	(std::string, value)
)

BOOST_FUSION_ADAPT_STRUCT(
	ChannelInputCommand,
	(InputCommand, input_command)
)

BOOST_FUSION_ADAPT_STRUCT(
	ChannelOutputCommand,
	(OutputCommand, cmd)
)

BOOST_FUSION_ADAPT_STRUCT(
	ChannelRegulatorCommand,
	(RegulatorCommand, cmd)
)

BOOST_FUSION_ADAPT_STRUCT(
	ChannelKeepCommand,
	(optional<double>, value)
)

BOOST_FUSION_ADAPT_STRUCT(
	ChannelCommand,
	(std::string, channel_name)
	(ChannelCommands, channel_command)
)

BOOST_FUSION_ADAPT_STRUCT(
	Command,
	(optional<Commands>, command)
)

namespace SRCLF {
namespace ClientRequestParsing {

	template <typename Iterator>
	struct command_parser : qi::grammar<Iterator, Command(), ascii::space_type >
	{
		command_parser() : command_parser::base_type(command)
		{				
			using qi::lit;		
			namespace ascii = boost::spirit::ascii;

			command = channel_command;

			channel_command = lit("channel") >> '[' >> identifier >> "]." >> (
				capture
				| release
				| keep
				| disable
				| channel_input_command
				| channel_output_command
				| channel_regulator_command 
				);

			capture = lit("capture") [_val = ChannelCaptureControlCommand()];
			release = lit("release") [_val = ChannelReleaseControlCommand()];
			keep = lit("keep") >> 
				-( 
				'(' >> double_ >> ')' 
					);
			disable = lit("disable") [_val = ChannelDisableCommand()];

			channel_regulator_command = lit("regulator") >> lit('.') >> regulator_command;
			channel_input_command = lit("input") >> lit('.') >> input_command;
			channel_output_command = lit("output") >> lit('.') >> output_command;


			regulator_command = (
				regulator_admin_command
				| regulator_control_command
				| regulator_info_command
				);

			//TODO: Unite with other admin-control-info commands
			regulator_admin_command = lit("admin") >> '(' >> quoted >> ')';
			regulator_control_command = lit("control") >> '(' >> quoted >> ')';
			regulator_info_command = lit("info") >> '(' >> quoted >> ')';

			input_command = (
				lit("get") [_val = InputGetCommand()] //input_get_command //lit("get") [_val = InputGetCommand()]
				| input_admin_command [_val = boost::spirit::_1]
				| input_control_command [_val = boost::spirit::_1]
				| input_info_command [_val = boost::spirit::_1]
				);
			
			input_admin_command = lit("admin") >> '(' >> quoted >> ')';
			input_control_command = lit("control") >> '(' >> quoted >> ')';
			input_info_command = lit("info") >> '(' >> quoted >> ')';


			output_command = (
				lit("get") [_val = OutputGetCommand()]
				| output_set_command [_val = boost::spirit::_1]
				| output_admin_command [_val = boost::spirit::_1]
				| output_control_command [_val = boost::spirit::_1]
				| output_info_command [_val = boost::spirit::_1]
				);
			
			output_set_command = lit("set") >> '(' >> qi::double_ >> ')';
			output_admin_command = lit("admin") >> '(' >> quoted >> ')';
			output_control_command = lit("control") >> '(' >> quoted >> ')';
			output_info_command = lit("info") >> '(' >> quoted >> ')';

			identifier = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z_0-9");
			quoted = lit('"') >> *(!qi::char_('*')) >> lit('"');	//TODO: add test case
		}

		qi::rule<Iterator, Command(), ascii::space_type > command;

		qi::rule<Iterator, ChannelCommand(), ascii::space_type > channel_command;
		qi::rule<Iterator, ChannelReleaseControlCommand(), ascii::space_type > release;
		qi::rule<Iterator, ChannelCaptureControlCommand(), ascii::space_type > capture;
		qi::rule<Iterator, ChannelInputCommand(), ascii::space_type > channel_input_command;
		qi::rule<Iterator, ChannelOutputCommand(), ascii::space_type > channel_output_command;
		qi::rule<Iterator, ChannelRegulatorCommand(), ascii::space_type > channel_regulator_command;
		qi::rule<Iterator, ChannelKeepCommand(), ascii::space_type > keep;
		qi::rule<Iterator, ChannelDisableCommand(), ascii::space_type > disable;

		qi::rule<Iterator, RegulatorCommand(), ascii::space_type > regulator_command;
		qi::rule<Iterator, RegulatorAdminCommand(), ascii::space_type > regulator_admin_command;
		qi::rule<Iterator, RegulatorControlCommand(), ascii::space_type > regulator_control_command;
		qi::rule<Iterator, RegulatorInfoCommand(), ascii::space_type > regulator_info_command;

		qi::rule<Iterator, InputCommand(), ascii::space_type > input_command;
		qi::rule<Iterator, InputAdminCommand(), ascii::space_type > input_admin_command;
		qi::rule<Iterator, InputControlCommand(), ascii::space_type > input_control_command;
		qi::rule<Iterator, InputInfoCommand(), ascii::space_type > input_info_command;

		qi::rule<Iterator, OutputCommand(), ascii::space_type > output_command;		
		qi::rule<Iterator, OutputSetCommand(), ascii::space_type > output_set_command;
		qi::rule<Iterator, OutputAdminCommand(), ascii::space_type > output_admin_command;
		qi::rule<Iterator, OutputControlCommand(), ascii::space_type > output_control_command;
		qi::rule<Iterator, OutputInfoCommand(), ascii::space_type > output_info_command;

		qi::rule<Iterator, std::string(), ascii::space_type > identifier;
		qi::rule<Iterator, std::string()> quoted;
	};

	bool CommandParser::parseCommand( std::string scmd , Command& cmd )
	{
		using boost::spirit::ascii::space;
		command_parser<std::string::const_iterator> parser;
		std::string::const_iterator begin = scmd.begin();
		std::string::const_iterator end = scmd.end();	
		if( !phrase_parse(begin, end, parser, space, cmd) )  
		{
			//Error
			return false;
		}
		else
		{
			//Ok
		}

		return true;
	}

} //ClientRequestParsing
} //namespace SRCLF

